package org.springframework.mybatis.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.springframework.annotation.mybatis.*;
import org.springframework.mybatis.utils.JDBCUtils;
import org.springframework.mybatis.utils.SqlUtils;

import java.lang.reflect.*;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>映射器mapper代理类</p>
 * @author Bosen
 * @date 2021/9/27 21:16
 */
public class MapperProxy implements MethodInterceptor {

    /**
     * <p>被代理的对象</p>
     */
    private Class<?> mapperInterface;

    public MapperProxy(Class<?> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(mapperInterface);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    /**
     * <p>查找</p>
     */
    private Object query(String sql, Method method, Object[] args) {
        try {
            // 获取sql上的参数名称
            List<String> sqlParams = SqlUtils.getParams(sql);
            // 获取方法上的参数信息
            Parameter[] params = method.getParameters();
            // 处理用户传递的参数信息，使其与sql语句上的信息位置对应
            List<Object> sortedParams = getSortedParams(params, sqlParams, args);
            // 使用占位符"？"替代sql语句中的变量
            sql = SqlUtils.occupied(sql);
            // 执行sql语句
            ResultSet rs = JDBCUtils.query(sql, sortedParams);
            if (!rs.next()) {
                return null;
            }
            rs.previous();

            // 获取返回类型
            Class<?> returnType = method.getReturnType();

            // 实例化返回类型对象
            Object result = returnType.newInstance();

            while (rs.next()) {
                Field[] fields = returnType.getDeclaredFields();
                for (Field field : fields) {
                    // 获取集合中数据
                    Object value = rs.getObject(field.getName());
                    // 设置允许私有访问
                    field.setAccessible(true);
                    // 赋值参数
                    field.set(result, value);
                }
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * <p>增删改</p>
     */
    private int execute(String sql, Method method, Object[] args) {
        try {
            // 获取sql上的参数名称
            List<String> sqlParams = SqlUtils.getParams(sql);
            // 获取方法上的参数信息
            Parameter[] params = method.getParameters();
            // 处理用户传递的参数信息，使其与sql语句上的信息位置对应
            List<Object> sortedParams = getSortedParams(params, sqlParams, args);
            // 使用占位符"？"替代sql语句中的变量
            sql = SqlUtils.occupied(sql);
            // 执行sql语句
            return JDBCUtils.execute(sql, sortedParams.toArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }


    /**
     * <p>排序参数，使其与sql语句的变量位置对应</p>
     */
    private List<Object> getSortedParams(Parameter[] parameters, List<String> sqlParams, Object[] args) throws Exception {

        // 获取方法上参数集合
        ConcurrentHashMap<String, Object> paramMap = new ConcurrentHashMap<>();
        for (int i = 0; i < parameters.length; i++) {
            // 参数信息
            Parameter parameter = parameters[i];
            // 获取参数名称
            Param param = parameter.getAnnotation(Param.class);
            if (param == null) {
                throw new Exception("请为Mapper的返回参数加上@Param注解~!!");
            }
            // 获取参数名
            String paramName = param.value().trim();
            if ("".equals(paramName)) {
                throw new Exception("请为@Param注解的value属性赋值~!!");
            }
            // 参数值
            Object object = args[i];
            paramMap.put(paramName, object);
        }

        // 排好序的参数
        List<Object> sortedParams = new ArrayList<>();

        for (String sqlParam : sqlParams) {
            Object object = paramMap.get(sqlParam);
            sortedParams.add(object);
        }
        return sortedParams;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object object = null;
        // 增
        Insert insert = method.getAnnotation(Insert.class);
        if (insert != null) {
            object = execute(insert.value().trim(), method, args);
        }
        // 删
        Delete delete = method.getAnnotation(Delete.class);
        if (delete != null) {
            object = execute(delete.value().trim(), method, args);
        }
        // 改
        Update update = method.getAnnotation(Update.class);
        if (update != null) {
            object = execute(update.value().trim(), method, args);
        }
        // 查
        Select select = method.getAnnotation(Select.class);
        if (select != null) {
            object = query(select.value().trim(), method, args);
        }
        return object;
    }
}
